tree grammar BTree;

options {
    tokenVocab = BParser;
    ASTLabelType = CommonTree;
    superClass = AbstractBTree;
}

@header {
    package com.nts.bupna;
    
    import com.nts.bupna.model.Process;
    import com.nts.bupna.model.*;
}

proc returns [Process result]
@init { Process process = new Process(); }
    : ^(PROCESS 
          named[process] 
          PACKAGE p=qId { process.setPackageName($p.result); }
          im=imports? { process.setImports($im.result); }
          v=variables { process.setVars($v.result); }
          r=receive { process.setReceive($r.result); }
          a=activity[process]
          rpl=reply? { process.setReply($rpl.result); } 
      ) {
    $result = process;
};

qId returns [String result] 
    : (r=ID_STRING | r=QID_STRING) {
    $result = $r.text;
};

named[Named named] 
    : NAME n=ID_STRING {
    named.setName($n.text);
};

typed[Typed typed] 
    : TYPE t=ID_STRING {
    typed.setType($t.text);
};

withInput[HasInput o] 
    : VAR v=ID_STRING { o.setVar($v.text); }
     | VARS vList=ID_LIST_STRING { o.setVars($vList.text); };


withOutput[HasOutput o] 
    : RESULT r=ID_STRING {
    o.setResult($r.text);
};

imports returns [Imports result]
scope { Imports theImports; }
@init { $imports::theImports = new Imports(); } 
    : ^(IMPORTS anImport+) {
    $result = $imports::theImports;
};

anImport 
    : s=STATIC? v=(QID_STRING | QID_STAR_STRING) {
    String staticPrefix = $s.text == null ? "" : $s.text + " ";
    $imports::theImports.add(staticPrefix + $v.text);
};

variables returns [Vars result]
scope { List<Var> varList; }
@init { $variables::varList = new ArrayList<Var>(); }
    :  ^(VARS variable+) { 
    $result = new Vars();
    $result.setVars($variables::varList);
};

variable 
@init { Var var = new Var();}
    : ^(VAR 
        named[var] 
        typed[var]
        (INIT v=ID_STRING { var.setInit($v.text);} )? 
        ) {
    $variables::varList.add(var);
};

receive returns [Receive result]
@init { $result = new Receive(); }
    : ^(RECEIVE withInput[result] named[result]);

invoke returns [Activity result] 
@init { Invoke invoke = new Invoke(); }
    : ^(INVOKE 
            typed[invoke] 
            METHOD m=ID_STRING {invoke.setMethod($m.text);} 
            withInput[invoke]?
            (METHOD_TYPE mt=ID_STRING { invoke.setMethodType($mt.text);} )? 
            (SERVICE s=ID_STRING { invoke.setService($s.text); })? 
            withOutput[invoke]?
       ) { 
    $result = invoke;
};

sequence returns [Activity result]
@init { Sequence sequence = new Sequence(); }
    : ^(SEQUENCE activity[sequence]+ ) {
    $result = sequence;
};

ifActivity returns [Activity result]
@init { If theIf = new If(); }
    : ^(IF
            CONDITION s=STRING {theIf.setCondition($s.text);}
            activity[theIf]
            elseif[theIf]*
            e=elseSub? { theIf.add($e.result); }
        ) {
     $result = theIf;
};

elseif[If theIf]
@init { If.ElseIf elseIf = new If.ElseIf();}
    : ^(ELSEIF 
            CONDITION s=STRING {elseIf.setCondition($s.text);}
            activity[elseIf]
       ) {
    theIf.add(elseIf);       
};
       
elseSub returns [If.Else result]
@init { $result = new If.Else();}
    : ^(ELSE activity[$result] );
    
whileActivity returns [Activity result]
@init { While theWhile = new While();}
    : ^(WHILE CONDITION s=STRING { theWhile.setCondition($s.text);} 
            activity[theWhile]
       ) {
     $result = theWhile;
};
    
activity[Container parent] 
    : (r=invoke | r=sequence | r=assign | r=ifActivity | r=whileActivity | r=throwActivity) {
    parent.add($r.result);
};

assign returns [Activity result]
@init {Assign assign = new Assign();}
    : ^(ASSIGN 
            (FROM f=qId {assign.setFrom($f.result);})?
            (EXPR s=STRING {assign.setExpr($s.text);})? 
            TO t=qId {assign.setTo($t.result);}
       ) {
    $result = assign;
};

reply returns [Reply result]
@init { $result = new Reply(); } 
    : ^(REPLY withOutput[result]);
    
throwActivity returns [Activity result]
@init { Throw theThrow = new Throw(); }
	: ^(THROW ERROR err=ID_STRING { theThrow.setError($err.text); }) {
	$result = theThrow;
};